classdef plotdat < handle
  % Container for moorings-related plot objects
  % Derived from handle
  % C-tor sig: fig = plotdat(G, t, P, T, S, D, O, RVID, text, QAcodes);
  %         all args required
  %     G: instance of class globals = container for RT global variables
  %     t, P, T, S, D, O, RVID: time, Pressure, Temperature, Salinity,
  %         Density, dissolved Oxygen, and RawValueIDs, resp.
  %     text: structure array containing titles and captions for the plots;
  %         see rt_driver for an example
  %     QAcodes: cell array of QAQC codes for the dependent variables; 
  %         may be empty
  %
  % Author: David Goldsmith, Wash. State Dept. of Ecology, dgol461@ecy.wa.gov
  % Release date: < 9/15/2011

  % TODO: Re-factor all the repetitive code into separate methods
    properties
      % Bottom-left corners and heighths and widths of the time series plots
        tserieslp = 0.1; tseriesbp = [0.8 0.65 0.5 0.35];
        tseriesw = 0.80; tseriesh = 0.15;
      % Default timeseries plots
        Pvst; Tvst; Svst; Ovst; RVIDvst;
      % "3-D" plots
        TvS_hist; OvS_hist; Tscat; Oscat;
      % Plot containers
        fh; hBrushLines; hAvLines;
      % Interactive-graphics-related objects
        brushObj; databox; zt; zih; zoh; zicb; zocb; pt; panh; pncb; dch; dccb; 
        brushedQA = struct('P', struct('data', [], 'mask', []), ...
                           'T', struct('data', [], 'mask', []), ...
                           'S', struct('data', [], 'mask', []), ...
                           'O', struct('data', [], 'mask', []));
      % Miscellany
        av_win; av_thresh; patches; safe_t; skip_contour = false;
        nPts; nQAlines; safeQAxes; safeQAplots; safe_tTicks;
        samplingInterval;
    end
    
    methods
        function fig = plotdat(G, t, P, T, S, D, O, RVID, text, QAcodes)
            
            if any(strcmp(G.siteID, {'SQX01BR'}))
                fig.samplingInterval = 1/48;
            else
                fig.samplingInterval = 1/96;
            end
            web = G.switches.web;
            if web || isempty(QAcodes)
                QAcodes = {{}, {}, {}, {}, {}, {}, {}, {}};
            else
                [DQV, DQF, DQC] = QAcodes{6:8};
                [fig.nPts, fig.nQAlines] = size(QAcodes{1});
            end
            fig.safe_t = t;
            set(0, 'Units', 'inches')
            scrnSz = get(0, 'ScreenSize');
            fig.fh = figure('units', 'inches',...
                            'color', 'w',...
                            'outerposition', [0.1*scrnSz(3) 0.05*scrnSz(4) ...
                                              0.8*scrnSz(3) 0.9*scrnSz(4)],...
                            'paperposition', [0 0 8.5 11],...
                            'visible', 'off',...
                            'renderer', 'zbuffer');

            fig.calculatePatches(G.t_interval);

            fig.av_win = G.av_win;
            fig.av_thresh = G.av_thresh;
            
          % RVID w/NaNs for Z Data
            fig.RVIDvst = tseries(fig, t, RVID, QAcodes{1}, G);
            fig.RVIDvst.placeNaNs();
          
          % Add DO plot first, despite being at the bottom of the "stack" of
          % time series plots, in order to cover the ticks otherwise
          % displayed along this plot's top border)
            fig.Ovst = tseries(fig, t, O, QAcodes{5}, G);
            fig.Ovst.placeNaNs();
            if (isnan(nanvar(fig.Ovst.Y)) || nanvar(fig.Ovst.Y) == 0)
              % Little trick to realize an "invisible" time series
                if web
                    fig.Ovst.Y = floor(fig.Ovst.twNaNs/max(fig.Ovst.twNaNs));
                    plotstr = ['obj.plot = plot(obj.twNaNs, obj.Y, ''.k'', ''MarkerSize'', 1'...
                               ', ''ZData'', obj.parent_fig.RVIDvst.YwNaNs);'];
                else
                    fig.Ovst.Y = floor(fig.Ovst.t/max(fig.Ovst.t));
                    plotstr = ['obj.plot = plot(obj.t, obj.Y, ''.k'', ''MarkerSize'', 1'...
                               ', ''ZData'', obj.parent_fig.RVIDvst.Y, '...
                               '''UserData'', ''O'');'];
                end
                annotation('textbox', [0.15 0.375 0.7 0.1], ...
                           'String', 'Variable currently not reported', ...
                           'LineStyle', 'none', 'fontsize', 24, 'color', [0 0 0.75], ...
                           'HorizontalAlignment', 'center', 'VerticalAlignment', 'middle');
                annotation('textbox', [0.90075, fig.tseriesbp(4), 0.1, 0.15], ...
                           'BackgroundColor', 'w', 'LineStyle', 'none');
                G.doDO = false;
            else % if isnan(nanvar(fig.Ovst.Y)) || nanvar(fig.Ovst.Y) == 0
                if web
                    plotstr = ['obj.plot = plot(obj.twNaNs, obj.YwNaNs, ''b'''...
                               ', ''ZData'', obj.parent_fig.RVIDvst.YwNaNs);'];
                else
                    plotstr = ['obj.plot = plot(obj.t, obj.Y, ''b'''...
                               ', ''ZData'', obj.parent_fig.RVIDvst.Y, '...
                               '''UserData'', ''O'');'];
                end
            end % if isnan(nanvar(fig.Ovst.Y)) || nanvar(fig.Ovst.Y) == 0
            position = [fig.tserieslp fig.tseriesbp(4) fig.tseriesw fig.tseriesh];
            if G.doDO && ~web
                fig.Ovst.QAplot([position(1:3) 0.03], 'O')
                fig.adjustScales('', gca)
                position = position + [0 0.03 0 -0.03];
            end
            
            fig.Ovst.tsplot(plotstr,... % defined above
                            position,... % position in figure
                            true); % add patches
            if ~G.doDO && ~web
                axis tight;
                fig.adjustScales('', gca)
            else
                axis tight;
            end

            if G.doDO
                fig.Ovst.computeAvs();
                line(fig.Ovst.tav, fig.Ovst.Yav, ...
                                   'Color', 'c', ...
                                   'LineStyle', '--', ...
                                   'LineWidth', 1.5, ...
                                   'Tag', 'AvgPlot');
                fig.Ovst.computeStats();
                annotation('textbox', [0.90075, fig.tseriesbp(4), 0.1, 0.15], 'BackgroundColor', 'w', ...
                           'String', ['mean:    ' num2str(fig.Ovst.stats.mean, '%3.1f') char(10) ...
                                      'stddev:  ' num2str(fig.Ovst.stats.stddev, '%3.1f') char(10) ...
                                      'max:     ' num2str(fig.Ovst.stats.max, '%3.1f') char(10) ...
                                      '75%-ile: ' num2str(fig.Ovst.stats.thrdqrtile, '%3.1f') char(10) ...
                                      'median:  ' num2str(fig.Ovst.stats.median, '%3.1f') char(10) ...
                                      '25%-ile: ' num2str(fig.Ovst.stats.frstqrtile, '%3.1f') char(10) ...
                                      'min:     ' num2str(fig.Ovst.stats.min, '%3.1f')], ...
                           'LineStyle', 'none', 'fontsize', 8, ...
                           'VerticalAlignment', 'middle');
                
            end % if G.doDO
            if web
                xlabel('(UTC)');
                xticks = ceil(G.t_interval(1)):1:G.t_interval(2);
                xticklabels = datestr(xticks, 'mm/dd');
                set(gca, 'XLim', G.t_interval, 'xtickmode', 'manual',...
                         'XTick', xticks, 'XTickLabel', xticklabels);
            elseif G.doDO
                set(gca, 'xtick', []);
            end
            annotation('textarrow', 'Position', [0.01 fig.tseriesbp(4)+0.075 0 0.15], ...
                       'String', 'Oxygen (mg/L)', 'fontsize', 10, ...
                       'HeadStyle', 'none', 'LineStyle', 'none', ...
                       'TextRotation', 90, 'HorizontalAlignment', 'center');
            Omin = min(fig.Ovst.Y); 
            Omax = max(fig.Ovst.Y); 
            Orange = Omax - Omin;
            Otickvals = Omin + Orange*[1 2 3 4 5]/6;
            Oticklabels = {sprintf('%4.1f', Otickvals(1)), '',...
                           sprintf('%4.1f', Otickvals(3)), '',...
                           sprintf('%4.1f', Otickvals(5))};
            set(gca, 'TickDir', 'out',...
                     'TickLength', [0.01 0.01],...
                     'ytick', Otickvals,...
                     'yticklabel', Oticklabels,...
                     'box', 'on');
            if web
              % Add missing data plot
                yaxlims = get(gca, 'YLim');
                [nbreaks,~] = size(fig.RVIDvst.breaks);
                if nbreaks
                    for i=1:nbreaks
                        x = fig.RVIDvst.breaks(i,1):fig.samplingInterval:fig.RVIDvst.breaks(i,2);
                        y = 0*x + yaxlims(1);
                        plot(x, y, '.', 'Color', [0 155 0]/255, 'MarkerSize', 12);
                    end % for i=1:nbreaks
                end % if nbreaks
            end
            
            fig.Pvst = tseries(fig, t, P, QAcodes{2}, G);
            fig.Pvst.placeNaNs();
            if isnan(nanvar(fig.Pvst.Y)) || nanvar(fig.Pvst.Y) == 0
                noP = true;
                fig.Pvst.Y = floor(fig.Pvst.twNaNs/max(fig.Pvst.twNaNs));
                plotstr = ['obj.plot = plot(obj.twNaNs, obj.Y, ''.k'', ''MarkerSize'', 1'...
                           ', ''ZData'', obj.parent_fig.RVIDvst.YwNaNs, '...
                           '''UserData'', ''P'');'];
                annotation('textbox', [0.15 0.825 0.7 0.1], ...
                           'String', 'Variable currently not reported', ...
                           'LineStyle', 'none', 'fontsize', 24, 'color', [0 0 0.75], ...
                           'HorizontalAlignment', 'center', 'VerticalAlignment', 'middle');
                annotation('textbox', [0.90075, fig.tseriesbp(1), 0.1, 0.15], ...
                           'BackgroundColor', 'w', 'LineStyle', 'none');
            else % if isnan(nanvar(fig.Pvst.Y)) || nanvar(fig.Pvst.Y) == 0
                plotstr = ['obj.plot = plot(obj.twNaNs, obj.YwNaNs, ''k'''...
                           ', ''ZData'', obj.parent_fig.RVIDvst.YwNaNs, '...
                           '''UserData'', ''P'');'];
                noP = false;
            end % if isnan(nanvar(fig.Pvst.Y)) || nanvar(fig.Pvst.Y) == 0
            Pmin = min(fig.Pvst.Y); Pmax = max(fig.Pvst.Y);
            if strcmp(G.siteID(end), 'F') && ~noP
                Pmin = Pmin - 3; Pmax = Pmax + 3;
            end
            position = [fig.tserieslp fig.tseriesbp(1) fig.tseriesw fig.tseriesh];
            if ~web && ~noP
                fig.Pvst.QAplot([position(1:3) 0.03], 'P')
                position = position + [0 0.03 0 -0.03];
            end
                        
            fig.Pvst.tsplot(plotstr,... % defined above
                            position,... % position in figure
                            true, [Pmin Pmax]); % add patches
                        
            if ~noP
                fig.Pvst.computeAvs();
                line(fig.Pvst.tav, fig.Pvst.Yav, ...
                                   'Color', 'k', ...
                                   'LineStyle', '--', ...
                                   'LineWidth', 1.5, ...
                                   'Tag', 'AvgPlot');
                fig.Pvst.computeStats();
                annotation('textbox', [0.90075, fig.tseriesbp(1), 0.1, 0.15], 'BackgroundColor', 'w', ...
                           'String', ['mean:     ' num2str(fig.Pvst.stats.mean, '%3.1f') char(10) ...
                                      'stddev:   ' num2str(fig.Pvst.stats.stddev, '%3.1f') char(10) ...
                                      'max:      ' num2str(fig.Pvst.stats.max, '%3.1f') char(10) ...
                                      '75%-ile:  ' num2str(fig.Pvst.stats.thrdqrtile, '%3.1f') char(10) ...
                                      'median:   ' num2str(fig.Pvst.stats.median, '%3.1f') char(10) ...
                                      '25%-ile:  ' num2str(fig.Pvst.stats.frstqrtile, '%3.1f') char(10) ...
                                      'min:      ' num2str(fig.Pvst.stats.min, '%3.1f')], ...
                           'LineStyle', 'none', 'fontsize', 8, ...
                           'VerticalAlignment', 'middle');
            end % if ~noP
            title(sprintf('%s %s (%s, %s)', text(:).Title), 'FontSize', 14);
            annotation('textarrow', 'Position', [0.01 fig.tseriesbp(1)+0.075 0 0.15], ...
                       'String', 'Pressure (dbar)', 'fontsize', 10, ...
                       'HeadStyle', 'none', 'LineStyle', 'none', ...
                       'TextRotation', 90, 'HorizontalAlignment', 'center');
            Prange = Pmax - Pmin;
            Ptickvals = Pmin + Prange*[1 2 3 4 5]/6;
            Pticklabels = {sprintf('%4.1f', Ptickvals(1)), '',...
                           sprintf('%4.1f', Ptickvals(3)), '',...
                           sprintf('%4.1f', Ptickvals(5))};
            set(gca, 'TickDir', 'out',...
                     'TickLength', [0.01 0.01],...
                     'xtick', [],...
                     'xlim', G.t_interval,...
                     'ylim', [Pmin Pmax],...
                     'ytick', Ptickvals,...
                     'yticklabel', Pticklabels,...
                     'box', 'on');
            axis tight;
            
            fig.Tvst = tseries(fig, t, T, QAcodes{3}, G);
            fig.Tvst.placeNaNs();
            if isnan(nanvar(fig.Tvst.Y)) || nanvar(fig.Tvst.Y) == 0
                noT = true;
                fig.Tvst.Y = floor(fig.Tvst.twNaNs/max(fig.Tvst.twNaNs));
                plotstr = ['obj.plot = plot(obj.twNaNs, obj.Y, ''.k'', ' ...
                           '''MarkerSize'', 1, ''ZData'', obj.parent_fig.RVIDvst.YwNaNs, '...
                           '''UserData'', ''T'');'];
                annotation('textbox', [0.15 0.675 0.7 0.1], ...
                           'String', 'Variable currently not reported', ...
                           'LineStyle', 'none', 'fontsize', 24, 'color', [0 0 0.75], ...
                           'HorizontalAlignment', 'center', 'VerticalAlignment', 'middle');
                annotation('textbox', [0.90075, fig.tseriesbp(2), 0.1, 0.15], ...
                           'BackgroundColor', 'w', 'LineStyle', 'none');
            else % if isnan(nanvar(fig.Tvst.Y)) || nanvar(fig.Tvst.Y) == 0
                plotstr = ['obj.plot = plot(obj.twNaNs, obj.YwNaNs, ''r'', ' ...
                           '''ZData'', obj.parent_fig.RVIDvst.YwNaNs, '...
                           '''UserData'', ''T'');'];
                noT = false;
            end % if isnan(nanvar(fig.Tvst.Y)) || nanvar(fig.Tvst.Y) == 0
            Tmin = min(fig.Tvst.Y); Tmax = max(fig.Tvst.Y);
            position = [fig.tserieslp fig.tseriesbp(2) fig.tseriesw fig.tseriesh];
            if ~web && ~noT
                fig.Tvst.QAplot([position(1:3) 0.03], 'T')
                position = position + [0 0.03 0 -0.03];
            end
            fig.Tvst.tsplot(plotstr,... % defined above
                            position,... % position in figure
                            true); % add patches
            axis tight;
            if ~noT
                fig.Tvst.computeAvs();
                line(fig.Tvst.tav, fig.Tvst.Yav, ...
                                   'Color', 'm', ...
                                   'LineStyle', '--', ...
                                   'LineWidth', 1.5, ...
                                   'Tag', 'AvgPlot');
                fig.Tvst.computeStats();
                annotation('textbox', [0.90075, fig.tseriesbp(2), 0.1, 0.15], 'BackgroundColor', 'w', ...
                           'String', ['mean:     ' num2str(fig.Tvst.stats.mean, '%3.1f') char(10) ...
                                      'stddev:   ' num2str(fig.Tvst.stats.stddev, '%3.1f') char(10) ...
                                      'max:      ' num2str(fig.Tvst.stats.max, '%3.1f') char(10) ...
                                      '75%-ile:  ' num2str(fig.Tvst.stats.thrdqrtile, '%3.1f') char(10) ...
                                      'median:   ' num2str(fig.Tvst.stats.median, '%3.1f') char(10) ...
                                      '25%-ile:  ' num2str(fig.Tvst.stats.frstqrtile, '%3.1f') char(10) ...
                                      'min:      ' num2str(fig.Tvst.stats.min, '%3.1f')], ...
                           'LineStyle', 'none', 'fontsize', 8, ...
                           'VerticalAlignment', 'middle');
            end % if ~noT
            annotation('textarrow', 'Position', [0.01 fig.tseriesbp(2)+0.075 0 0.15], ...
                       'String', 'Temperature (\circC)', 'fontsize', 10, ...
                       'HeadStyle', 'none', 'LineStyle', 'none', ...
                       'TextRotation', 90, 'HorizontalAlignment', 'center');
            Trange = Tmax - Tmin;
            Ttickvals = Tmin + Trange*[1 2 3 4 5]/6;
            Tticklabels = {sprintf('%4.1f', Ttickvals(1)), '',...
                           sprintf('%4.1f', Ttickvals(3)), '',...
                           sprintf('%4.1f', Ttickvals(5))};
            set(gca, 'TickDir', 'out',...
                       'TickLength', [0.01 0.01],...
                       'xtick', [],...
                       'xlim', G.t_interval,...
                       'ytick', Ttickvals,...
                       'yticklabel', Tticklabels,...
                       'box', 'on')
            
            fig.Svst = tseries(fig, t, S, QAcodes{4}, G);
            fig.Svst.placeNaNs();
            if isnan(nanvar(fig.Svst.Y)) || nanvar(fig.Svst.Y) == 0
                noS = true;
                fig.Svst.Y = floor(fig.Svst.twNaNs/max(fig.Svst.twNaNs));
                plotstr = ['obj.plot = plot(obj.twNaNs, obj.Y, ''.k'', ' ...
                           '''MarkerSize'', 1, ''ZData'', obj.parent_fig.RVIDvst.YwNaNs, ' ...
                           '''UserData'', ''S'');'];                
                fig.skip_contour = true;
                annotation('textbox', [0.15 0.525 0.7 0.1], ...
                           'String', 'Variable currently not reported', ...
                           'LineStyle', 'none', 'fontsize', 24, 'color', [0 0 0.75], ...
                           'HorizontalAlignment', 'center', 'VerticalAlignment', 'middle');
                annotation('textbox', [0.90075, fig.tseriesbp(3), 0.1, 0.15], ...
                           'BackgroundColor', 'w', 'LineStyle', 'none');
            else % if isnan(nanvar(fig.Svst.Y)) || nanvar(fig.Svst.Y) == 0
                plotstr = ['obj.plot = plot(obj.twNaNs, obj.YwNaNs, ' ...
                           '''ZData'', obj.parent_fig.RVIDvst.YwNaNs, ' ...
                           '''UserData'', ''S'');'];
                noS = false;
            end % if isnan(nanvar(fig.Svst.Y)) || nanvar(fig.Svst.Y) == 0
            Smin = min(fig.Svst.Y); Smax = max(fig.Svst.Y);
            position = [fig.tserieslp fig.tseriesbp(3) fig.tseriesw fig.tseriesh];
            if ~web && ~noS
                fig.Svst.QAplot([position(1:3) 0.03], 'S')
                position = position + [0 0.03 0 -0.03];
            end
            fig.Svst.tsplot(plotstr,... % defined above
                            position,... % position in figure
                            true); % add patches
            tmp = get(fig.Svst.axis, 'Children');
            for i=1:length(tmp)
                try
                    c = get(tmp(i), 'color');
                    if isequal(c, [0 0 1]) || isequal(c, [0 0 0])
                        tmp = tmp(i);
                        break
                    end
                catch
                end % try
            end % for i=1:length(tmp)
            set(tmp, 'color', [0.87 0.49 0]);
            axis tight;
            if ~noS
                fig.Svst.computeAvs();
                h = line(fig.Svst.tav, fig.Svst.Yav, ...
                                       'Color', [0.9 0.6 0], ...
                                       'LineStyle', '--', ...
                                       'LineWidth', 1.5, ...
                                       'Tag', 'AvgPlot');
                fig.Svst.computeStats();
                annotation('textbox', [0.90075, fig.tseriesbp(3), 0.1, 0.15], 'BackgroundColor', 'w', ...
                           'String', ['mean:     ' num2str(fig.Svst.stats.mean, '%3.1f') char(10) ...
                                      'stddev:   ' num2str(fig.Svst.stats.stddev, '%3.1f') char(10) ...
                                      'max:      ' num2str(fig.Svst.stats.max, '%3.1f') char(10) ...
                                      '75%-ile:  ' num2str(fig.Svst.stats.thrdqrtile, '%3.1f') char(10) ...
                                      'median:   ' num2str(fig.Svst.stats.median, '%3.1f') char(10) ...
                                      '25%-ile:  ' num2str(fig.Svst.stats.frstqrtile, '%3.1f') char(10) ...
                                      'min:      ' num2str(fig.Svst.stats.min, '%3.1f')], ...
                           'LineStyle', 'none', 'fontsize', 8, ...
                           'VerticalAlignment', 'middle');
            end % if ~noS
            annotation('textarrow', 'Position', [0.01 fig.tseriesbp(3)+0.075 0 0.15], ...
                       'String', 'Salinity (PSU)', 'fontsize', 10, ...
                       'HeadStyle', 'none', 'LineStyle', 'none', ...
                       'TextRotation', 90, 'HorizontalAlignment', 'center');
            Srange = Smax - Smin;
            Stickvals = Smin + Srange*[1 2 3 4 5]/6;
            Sticklabels = {sprintf('%4.1f', Stickvals(1)), '',...
                           sprintf('%4.1f', Stickvals(3)), '',...
                           sprintf('%4.1f', Stickvals(5))};
            set(gca, 'TickDir', 'out',...
                       'TickLength', [0.01 0.01],...
                       'xtick', [],...
                       'xlim', G.t_interval,...
                       'ytick', Stickvals,...
                       'yticklabel', Sticklabels,...
                       'box', 'on');
                   
            linkaxes([fig.Pvst.axis fig.Pvst.QAaxis ...
                      fig.Ovst.axis fig.Ovst.QAaxis ...
                      fig.Tvst.axis fig.Tvst.QAaxis ...
                      fig.Svst.axis fig.Svst.QAaxis ...
                     ], 'x')

            if ~web 
              % Assign myDataCursor as the ButtonDownFcn callback for the timeseries plots  
                tsObjects = findobj(fig.fh, '-regexp', 'Tag', '([P,p]lot)$');
                for o = tsObjects(:)'
                    set(o, 'ButtonDownFcn', {@fig.myDataCursor})
                end
            end
                    
            if any(~isnan(D))
                nS = 20; nT = 20;
                levels = [0.05 0.25 0.5 0.75 0.95];
                SM = fig.Svst.stats.max; Sm = fig.Svst.stats.min; Srange = SM - Sm;
                TM = fig.Tvst.stats.max; Tm = fig.Tvst.stats.min; Trange = TM - Tm;
                tol = 0.2;
                Dofdata = outlierFilter(D); 
                range = max(Dofdata)-min(Dofdata);
                incrs = [0.25 0.5 1.0 1.5]; 
                ranges = 5*incrs;
                dr = abs(ranges-range);
                incr = incrs(dr == min(dr));
                Sbins = linspace(10, 35, nS);
                Tbins = linspace(0, 25, nT);
                [SxS, TxT] = meshgrid(Sbins, Tbins);
                D = sw_dens0(SxS, TxT) - 1000;
                minD = round2(min(min(Dofdata)), incr, 'floor'); 
                maxD = round2(max(max(Dofdata)), incr, 'ceil');
                if web
                  % (Water) Density(S,T) and CDF(S,T) contours
                    fig.TvS_hist = axes('position', [fig.tserieslp 0.115 0.3 0.175], 'color', 'k', 'layer', 'bottom');
                    hold;
                    if ~fig.skip_contour
                        twoDHist(gca, fig.Svst.YwNaNs, fig.Tvst.YwNaNs,...
                                    [(Sm - tol*Srange) (SM + tol*Srange) nS],...
                                    [(Tm - tol*Trange) (TM + tol*Trange) nT],...
                                     levels);
                        [C, hc] = contour(SxS, TxT, D, minD:incr:maxD);
                        set(gca, 'TickDir', 'out',...
                                 'TickLength', [0.01 0.01]);
                        set(hc, 'LineColor', 'w');
                        clabelH = clabel(C, hc, 'LabelSpacing', 108);
                        set(clabelH(:), 'FontSize', 8,...
                                        'Color', 'w');
                        set(gca, 'Box', 'on');
                        title('Density (kg/m^3)');
                        xlabel('Salinity (PSU)');
                        annotation('textarrow', 'Position', [0.01 0.2 0 0.15], ...
                                   'String', 'Temperature (\circC)', 'fontsize', 10, ...
                                   'HeadStyle', 'none', 'LineStyle', 'none', ...
                                   'TextRotation', 90, 'HorizontalAlignment', 'center');
                        tmp = get(gca, {'XLim', 'YLim', 'XTick', 'YTick', 'XTickLabel', 'YTickLabel'});
                        [xlims, ylims, xticks, yticks, xtlabs, ytlabs] = tmp{:};
                        cmap = colormap; 
                        cmapres = length(cmap(:,1)); 
                        cs = colorscale([1 cmapres], [levels(1) levels(end)], 0.05, 'vertical',...
                             'position', [0.88 0.115 0.02 0.175], 'tickdir', 'out');
                        axis tight;
                        csticklabels = cellstr(num2str(levels(:)));
                        csticks = cmapres*levels; 
                        set(cs, 'YTick', csticks, 'YTickLabel', csticklabels, 'FontSize', 8,...
                            'Box', 'on', 'YaxisLocation', 'right');
                        ylabelH = get(cs, 'YLabel');
                        pos = get(ylabelH, 'Position');
                        set(ylabelH, 'String', 'Probability', 'FontSize', 9, 'Position', [3.3 pos(2:3)]);%[-3 pos(2:3)]);
                        if G.doDO
                            fig.OvS_hist = axes('position', [0.53 0.115 0.3 0.175], 'color', 'k', 'layer', 'bottom');
                            hold;
                            nO = 20;
                            OM = max(fig.Ovst.YwNaNs); Om = min(fig.Ovst.YwNaNs); Orange = OM - Om;
                            tol = 0.2;
                            twoDHist(gca, fig.Svst.YwNaNs, fig.Ovst.YwNaNs,...
                                        [(Sm - tol*Srange) (SM + tol*Srange) nS],...
                                        [(Om - tol*Orange) (OM + tol*Orange) nO],...
                                         levels);
                            set(gca, 'TickDir', 'out',...
                                     'TickLength', [0.01 0.01]);
                            set(hc, 'LineColor', 'w');
                            clabelH = clabel(C, hc, 'LabelSpacing', 108);
                            set(clabelH(:), 'FontSize', 8,...
                                            'Color', 'w');
                            set(gca, 'Box', 'on');
                            xlabel('Salinity (PSU)');
                            annotation('textarrow', 'Position', [0.45 0.2 0 0.15], ...
                                       'String', 'Dissolved Oxygen (mg/L)', 'fontsize', 10, ...
                                       'HeadStyle', 'none', 'LineStyle', 'none', ...
                                       'TextRotation', 90, 'HorizontalAlignment', 'center');
                            tmp = get(gca, {'XLim', 'YLim', 'XTick', 'YTick', 'XTickLabel', 'YTickLabel'});
                            [xlims, ylims, xticks, yticks, xtlabs, ytlabs] = tmp{:};
                            linkaxes([fig.TvS_hist fig.OvS_hist], 'x')
                        end % if G.doDO
                    end % if ~fig.skip_contour

                  % Caption
                    [caption, r] = strtok(text(1).Caption, ';');
                    caption = strrep(caption, '__', num2str(24*fig.av_win));
                    [c, r] = strtok(r, ';');
                    caption = [caption char(10) '    ' c];
                    [c, r] = strtok(r, ';');
                    caption = [caption char(10) c];
                    if ~isempty(O) && any(~isnan(O))
                        [c, r] = strtok(r, ';');
                        caption = [caption char(10) c];
                    end
                    annotation('textbox', [0.05 0.025 0.9 0.05], 'String', caption,...
                               'LineStyle', 'none', 'fontsize', 8);

                else % if ~web

                    if ~isempty(fig.Tvst.YwNaNs) && any(~isnan(fig.Tvst.YwNaNs))
                        fig.Tscat = scat(fig, fig.Svst.YwNaNs, ...
                                                  fig.Tvst.YwNaNs, ...
                                                  fig.RVIDvst.YwNaNs, ...
                                                  G);
                        if ~fig.skip_contour
                            plotstr = ['obj.plot = plot(obj.X, obj.Y, ''ok'', ' ...
                                       '''ZData'', obj.Z, ' ...
                                       '''MarkerSize'', 4, ' ...
                                       '''MarkerFaceColor'', ''k'', '...
                                       '''Tag'', ''DataAxes'');'];
                            fig.Tscat.scplot(plotstr, [fig.tserieslp 0.115 0.3 0.175])
                            hold;
                            fig.Tscat.xLimsWithOutliers = get(fig.Tscat.axis, 'XLim');
                            fig.Tscat.yLimsWithOutliers = get(fig.Tscat.axis, 'YLim');
                            Stmp = outlierFilter(S);
                            Sbuf = 0.05 * (max(Stmp) - min(Stmp));
                            fig.Tscat.xLimsSansOutliers = ...
                                [min(Stmp) max(Stmp)] + Sbuf * [-1 1];
                            xlim(fig.Tscat.xLimsSansOutliers);
                            Ttmp = outlierFilter(T);
                            Tbuf = 0.05 * (max(Ttmp) - min(Ttmp));
                            fig.Tscat.yLimsSansOutliers = ...
                                [min(Ttmp) max(Ttmp)] + Tbuf * [-1 1];
                            ylim(fig.Tscat.yLimsSansOutliers);

                          % Plot density contours
                            [C, h] = contour(SxS, TxT, D, minD:incr:maxD);
                            set(h, 'LineColor', [0.35 0.35 0.35]);
                            clabelH = clabel(C, h, 'LabelSpacing', 108);
                            set(clabelH(:), 'FontSize', 8,...
                                            'Color', [0.35, 0.35, 0.35]);
                            title('Density (kg/m^3)');
                            xlabel('Salinity (PSU)');
                            annotation('textarrow', 'Position', [0.01 0.2 0 0.15], ...
                                       'String', 'Temperature (\circC)', 'fontsize', 10, ...
                                       'HeadStyle', 'none', 'LineStyle', 'none', ...
                                       'TextRotation', 90, 'HorizontalAlignment', 'center');
                        end % if ~fig.skip_contour
                    end % if ~isempty(fig.Tvst.YwNaNs) && any(~isnan(fig.Tvst.YwNaNs))
                  % Dissolved Oxygen(S,T) scatter plot
                    if ~isempty(fig.Ovst.YwNaNs) && any(~isnan(fig.Ovst.YwNaNs))
                        fig.Oscat = scat(fig, fig.Svst.YwNaNs, ...
                                                  fig.Ovst.YwNaNs, ...
                                                  fig.RVIDvst.YwNaNs, ...
                                                  G);
                        if ~fig.skip_contour
                            plotstr = ['obj.plot = plot(obj.X, obj.Y, ''ok'', ' ...
                                       '''ZData'', obj.Z, ' ...
                                       '''MarkerSize'', 4, ' ...
                                       '''MarkerFaceColor'', ''k'');'];
                            fig.Oscat.scplot(plotstr, [0.6 0.115 0.3 0.175])
                            hold;
                            fig.Oscat.xLimsWithOutliers = ...
                                fig.Tscat.xLimsWithOutliers;
                            fig.Oscat.xLimsSansOutliers = ...
                                fig.Tscat.xLimsSansOutliers;
                            fig.Oscat.yLimsWithOutliers = get(fig.Oscat.axis, 'YLim');
                            Otmp = outlierFilter(O);
                            Obuf = 0.05 * (max(Otmp) - min(Otmp));
                            fig.Oscat.yLimsSansOutliers = ...
                                [min(Otmp) max(Otmp)] + Obuf * [-1 1];
                            xlim(fig.Oscat.xLimsSansOutliers);
                            ylim(fig.Oscat.yLimsSansOutliers);
                            xlabel('Salinity (PSU)');
                            annotation('textarrow', 'Position', [0.49 0.2 0 0.15], ...
                                       'String', 'Dissolved Oxygen (mg/L)', 'fontsize', 10, ...
                                       'HeadStyle', 'none', 'LineStyle', 'none', ...
                                       'TextRotation', 90, 'HorizontalAlignment', 'center');
                        end % if ~fig.skip_contour
                        linkaxes([fig.Tscat.axis fig.Oscat.axis], 'x')
                    end % if ~isempty(fig.Ovst.YwNaNs) && any(~isnan(fig.Ovst.YwNaNs))
                  % Finally, add rest of interactive stuff if not the Web version
 
                  % Define a context menu for toggling between outliers filtered
                  % and unfiltered; note: it is not attached to anything yet
                    scatCmenu = uicontextmenu;
                  % Define the context menu items and install their callbacks
                    item1 = uimenu(scatCmenu, 'Label', 'Include Outliers',...
                                   'Callback', {@fig.zoomRelOutliers, 'with'});
                    item2 = uimenu(scatCmenu, 'Label', 'Exclude Outliers',...
                                   'Callback', {@fig.zoomRelOutliers, 'sans'});
                  % Attach the context menu to the axes
                    set(fig.Tscat.axis, 'uicontextmenu', scatCmenu)
                    if isprop(fig, 'Oscat') && isprop(fig.Oscat, 'axis')
                        set(fig.Oscat.axis, 'uicontextmenu', scatCmenu)
                    end
                  
                    mh = uimenu(fig.fh, 'Label', 'Mooring QAQC');
                    uimenu(mh, 'Label', 'Brush On', ...
                               'Callback', {@fig.brush_on});
                    uimenu(mh, 'Label', 'Brush Off', ...
                               'Callback', 'brush off');
                    uimenu(mh, 'Label', 'Link Selection', ...
                               'Callback', {@fig.disp_var});
                    uimenu(mh, 'Label', 'Export all', ...
                               'Callback', {@fig.exportData, 'all'}, ...
                               'Enable', 'off');

                  % Pan & Zoom Control
                    fig.panh = findall(fig.fh, 'TooltipString', 'Pan'); % the Pan uitoggletool
                    fig.pncb = get(fig.panh, 'ClickedCallback'); % get its default handler before overriding
                    set(fig.panh, 'ClickedCallback', {@fig.myPanClickedCallback}); % then override it

                    fig.zih = findall(fig.fh, 'TooltipString', 'Zoom In'); % the Zoom In uitoggletool
                    fig.zicb = get(fig.zih, 'ClickedCallback'); % get its default handler before overriding
                    set(fig.zih, 'ClickedCallback', {@fig.myZIClickedCallback}); % then override it

                    fig.zoh = findall(fig.fh, 'TooltipString', 'Zoom Out'); % the Zoom Out uitoggletool
                    fig.zocb = get(fig.zoh, 'ClickedCallback'); % get its default handler before overriding
                    set(fig.zoh, 'ClickedCallback', {@fig.myZOClickedCallback}); % then override it
                    % Make time axis the figure's current axis
                    plots = findobj(fig.fh, '-property', 'Xtick');
                    xticks = get(plots, 'Xtick');
                    compar = num2cell(zeros(1,numel(xticks)) + 7e5)';
                    ca = plots(~cellfun(@isempty, xticks) & ...
                               cellfun(@all, cellfun(@gt, xticks, compar, 'UniformOutput', 0)));
                    set(fig.fh, 'CurrentAxes', ca)

                    zoom reset
                    fig.myZOClickedCallback
                    set(fig.fh, 'pointer', 'circle')
                end % if web

            end % if ~isempty(D)

            set(fig.fh, 'visible', 'on')
            
        end % function fig = plotdat
        
        function delete(obj)
            clear get(obj);
        end
        
        function calculatePatches(obj, t_interval)
          % Calculate the starts and ends of the night-indicating patches
            sv = datevec(t_interval(1)); 
            shour = sv(4);
            if (2 < shour) && (shour <= 14) % if start is during the night (locally)
              % First patch begins immediately, subsequent patches begin at hour 2, starting tomorrow
                patchstarts = [t_interval(1) datenum([sv(1:2) sv(3)+1 2 0 0]):1:t_interval(2)];
              % All patches-except perhaps the last, to be dealt w/ in next code block-end at hour 14,                % starting today
                patchends = datenum([sv(1:3) 14 0 0]):1:t_interval(2);
            else % start is during the day locally
              % Patches begin at hour 2, starting w/ the next one (which may be today 
              % or tomorrow, depending on where t_interval(1) falls rel. midnight)
              % Patches end at hour 14, ditto, plus ditto comment above about end of last patch
                if shour <= 2 % it's after midnight, start and end patches begin today
                    patchstarts = datenum([sv(1:3) 2 0 0]):1:t_interval(2);
                    patchends = datenum([sv(1:3) 14 0 0]):1:t_interval(2);
                else % it's before midnight, start and end patches begin tomorrow
                    patchstarts = datenum([sv(1:2) sv(3)+1 2 0 0]):1:t_interval(2);
                    patchends = datenum([sv(1:2) sv(3)+1 14 0 0]):1:t_interval(2);
                end
            end
            try
                if patchstarts(end) > patchends(end) % if there isn't an end for the last patchstart
                    patchends = [patchends t_interval(2)];
                end
                obj.patches = {patchstarts, patchends};
            catch
                obj.patches = {};
            end
        end
        
        function addpatches(obj, ymin, ymax)
          % Function to draw the nighttime-indicating patches
            if ~isempty(obj.patches)
                ps = obj.patches{1};
                pe = obj.patches{2};
                pcol = [0.85 0.85 0.85];
                for i=1:length(ps)
                    patch([ps(i) pe(i) pe(i) ps(i)],...
                          [ymin ymin ymax ymax],...
                          pcol, 'edgecolor', 'none');
                end
                hold;
            end
        end
        
        function myDataCursor(varargin)
            obj = varargin{1}(1);
            line = varargin{2};
            ud = get(line, 'UserData');
            ax = get(line, 'Parent');
            cp = get(ax, 'CurrentPoint');
            data = get(line, {'XData', 'YData', 'ZData'});
            x = data{1}; y = data{2}; z = data{3};
            if strcmp(get(line, 'Tag'), 'DataPlot')
                qaline = findobj('Tag', 'QAplot', '-and', 'UserData', ud);
                c = get(qaline, 'CData');
                n = length(x);
                qactl = c(1:n,1); 
                qaflg = c(n+1:2*n,1); 
                qaval = c(2*n+1:end,1);
            elseif strcmp(get(line, 'Tag'), 'QAplot')
                c = get(line, 'CData');
                c = c(:,1)';
                n = length(c)/3;
                y = y(:,1)';
                x = x(1:n,1); 
                z = z(1:n,1);
                qactl = c(1:n); 
                qaflg = c(n+1:2*n); 
                qaval = c(2*n+1:end);
            end
            tmp = abs(x - cp(1,1));
            where = find(tmp == min(tmp));
            if z
                cursor = plot3(ax, x(where), y(where), z(where), 'g+');
                text = datestr(x(where), 't: mm/dd HH:MM  ');
                if length(obj.Pvst.Y) >= where && obj.Pvst.Y(where)
                    text = [text sprintf('P: %5.2f   ', obj.Pvst.Y(where))];
                end
                if length(obj.Tvst.Y) >= where && obj.Tvst.Y(where)
                    text = [text sprintf('T: %5.2f   ', obj.Tvst.Y(where))];
                end
                if length(obj.Svst.Y) >= where && obj.Svst.Y(where)
                    text = [text sprintf('S: %5.2f', obj.Svst.Y(where))];
                end
                if length(obj.Ovst.Y) >= where && obj.Ovst.Y(where)
                    text = [text sprintf('   DO: %5.2f', obj.Ovst.Y(where))];
                end
                if length(qaval) >= where
                    text = [text char(10)];
                    text = [text sprintf('QA Value: %d   ',   qaval(where))];
                    text = [text sprintf('QA Flag: %d   ',    qaflg(where))];
                    text = [text sprintf('QA Control: %d',    qactl(where))];
                end
            else
                cursor = plot3(ax, x(where), y(where), 0, 'g+');
                text = datestr(x(where), 'tav: mm/dd HH:MM  ');
                if length(obj.Pvst.Yav) >= where
                    text = [text sprintf('Pav: %5.2f   ', obj.Pvst.Yav(where))];
                end
                if length(obj.Tvst.Yav) >= where
                    text = [text sprintf('Tav: %5.2f   ', obj.Tvst.Yav(where))];
                end
                if length(obj.Svst.Yav) >= where
                    text = [text sprintf('Sav: %5.2f', obj.Svst.Yav(where))];
                end
                if length(obj.Ovst.Yav) >= where
                    text = [text sprintf('   DOav: %5.2f', obj.Ovst.Yav(where))];
                end
            end
            set(cursor, 'LineWidth', 2, 'MarkerSize', 8)

            if ~isempty(obj.databox)
                delete(obj.databox)
            end
            obj.databox = annotation(obj.fh, 'textbox', [0.5 0.05 0 0], ...
                                             'String', text, ...
                                             'FontSize', 9, ...
                                             'HorizontalAlignment', 'center', ...
                                             'VerticalAlignment', 'middle');
            set(obj.databox, 'FitBoxToText', 'on')
        end
        
        function myZIClickedCallback(varargin)
            obj = varargin{1};
            eval(obj.zicb);
            obj.zt = zoom(obj.fh);
            qaa = findall(obj.fh, 'Tag', 'QAaxes');
            setAxesZoomMotion(obj.zt, qaa, 'horizontal');
            set(obj.zt, 'ActionPostCallback', @obj.adjustScales);
        end
        
        function myZOClickedCallback(varargin)
            obj = varargin{1};
            if strcmp(get(obj.dch, 'SelectionType'), 'normal') && ... % single click
               strcmp(get(obj.zoh, 'State'), 'off') % Zoom Out tool not active
                return
            else
                if ~strcmp(get(get(0, 'CallbackObject'), 'Type'), 'uicontrol')
                    eval(obj.zocb);
                end
                zoom out
            end
        end
        
        function zoomRelOutliers(varargin)
            fig = varargin{1}; 
            switch varargin{4}
                case 'with'
                    set(fig.Tscat.axis, 'XLim', fig.Tscat.xLimsWithOutliers,...
                                        'YLim', fig.Tscat.yLimsWithOutliers)
                    if isprop(fig, 'Oscat') && isprop(fig.Oscat, 'axis')
                        set(fig.Oscat.axis, 'YLim', fig.Oscat.yLimsWithOutliers)
                    end

                case 'sans'
                    set(fig.Tscat.axis, 'XLim', fig.Tscat.xLimsSansOutliers,...
                                        'YLim', fig.Tscat.yLimsSansOutliers)
                    if isprop(fig, 'Oscat') && isprop(fig.Oscat, 'axis')
                        set(fig.Oscat.axis, 'YLim', fig.Oscat.yLimsSansOutliers)
                    end
                    
            end
        end
        
        function myPanClickedCallback(varargin)
            obj = varargin{1};
            eval(obj.pncb);
            obj.pt = pan(obj.fh);
            qaa = findall(obj.fh, 'Tag', 'QAaxes');
            setAxesPanMotion(obj.pt, qaa, 'horizontal');
            set(obj.pt, 'ActionPostCallback', @obj.adjustScales);
        end
        
        function datetick(obj, ax, fmt)
            datetick(ax, 'x', fmt, 'keepticks', 'keeplimits');
            tixwlblsnlims = get(ax, {'XTick', 'XTickLabel', 'XLim'});
            tix = tixwlblsnlims{1};
            lbls = cellstr(tixwlblsnlims{2});
            lims = tixwlblsnlims{3};
            visibleTix = find(lims(1) < tix & tix < lims(2));
            tix = tix(visibleTix);
            lbls = lbls(visibleTix);            
            switch fmt
                case 'mm'
                    tmp = datestr(tix(1), 'yy/mm');
                case 'dd'
                    tmp = datestr(tix(1), 'mm/dd');
                case 'dd HH PM'
                    tmp = datestr(tix(1), 'mm/dd HH PM');
                case 'HH PM'
                    tmp = datestr(tix(1), 'mm/dd HH PM');
                otherwise
                    tmp = datestr(tix(1), 'mm/dd HH:MM PM');
            end
            lbls{1} = tmp;
            set(ax, 'XTick', tix, 'XTickLabel', lbls)
        end
        
        function adjustScales(varargin)
            obj = varargin{1}(1);
          % First adjust y-axis (y-axes if zoom out)
            if strcmp(get(obj.zih, 'State'), 'on') % if we got here from zoom in
                ca = varargin{3};
                if isstruct(ca) && isfield(ca, 'Axes')
                    ca = ca.Axes;
                end
                checkAxesType = get(ca, 'Tag');
                if strcmp(checkAxesType, 'DataAxes')
                    ylim = get(ca, 'YLim');
                    newYticks = ylim(1):(ylim(2)-ylim(1))/4:ylim(2);
                    if any(round2(newYticks(2:3), 0.01) == round2(newYticks(3:4), 0.01))
                        fmt = '%0.3f';
                    else
                        fmt = '%0.2f';
                    end
                    set(ca, 'YTick', newYticks(2:4), 'YTickLabel', num2str(newYticks(2:4)', fmt));
                end
            else % otherwise, assume got here from zoom out
                AX = findall(obj.fh, 'Tag', 'DataAxes');
                if ~isempty(AX)
                    for ax = AX'
                        ylim = get(ax, 'YLim');
                        newYticks = ylim(1):(ylim(2)-ylim(1))/4:ylim(2);
                        set(ax, 'YTick', newYticks(2:4), 'YTickLabel', num2str(newYticks(2:4)','%0.1f'));
                    end
                end
            end
          % Now adjust x-axis
            if isempty(obj.Ovst.QAaxis)
                ca = obj.Ovst.axis;
            else
                ca = obj.Ovst.QAaxis;
            end
            xlim = get(ca, 'XLim');
            xrng = floor(96*(xlim(2) - xlim(1)));
            switch 1
                case xrng > 90*96 % xrng > 90 days
                    set(ca, 'XTick', 30*ceil(xlim(1)/30):30:xlim(2)) % ticks every 30 days
                    obj.datetick(ca, 'mm')
                case xrng > 45*96 % 45 dys < xrng < 90 days
                    set(ca, 'XTick', 15*ceil(xlim(1)/15):15:xlim(2)) % ticks every 15 days
                    obj.datetick(ca, 'dd')
                case xrng > 21*96 % 3 wks < xrng < 45 days
                    set(ca, 'XTick', 7*ceil(xlim(1)/7):7:xlim(2)) % ticks every 7 days
                    obj.datetick(ca, 'dd')
                case xrng > 14*96 % 2 wks < xrng < 3 wks
                    set(ca, 'XTick', 3*ceil(xlim(1)/3):3:xlim(2)) % ticks every 3 days
                    obj.datetick(ca, 'dd')
                case xrng > 7*96 % 1 wks < xrng < 2 wks
                    set(ca, 'XTick', 2*ceil(xlim(1)/2):2:xlim(2)) % ticks every 2 days
                    obj.datetick(ca, 'dd')
                case xrng > 3*96 % 3 dys < xrng < 1 week
                    set(ca, 'XTick', ceil(xlim(1)):1:xlim(2)) % ticks every day
                    obj.datetick(ca, 'dd')
                case xrng > 96 % 1 dy < xrng < 3 dys
                    set(ca, 'XTick', ceil(2*xlim(1))/2:0.5:xlim(2)) % ticks every 12 hours
                    obj.datetick(ca, 'dd HH PM')
                case xrng > 36 % 9 hrs < xrng < 1 dy
                    set(ca, 'XTick', ceil(8*xlim(1))/8:0.125:xlim(2)) % ticks every 3 hours
                    obj.datetick(ca, 'HH PM')
                case xrng > 12 % 3 hrs < xrng < 9 hrs
                    set(ca, 'XTick', ceil(24*xlim(1))/24:1/24:xlim(2)) % ticks every hours
                    obj.datetick(ca, 'HH:MM PM')
                otherwise % xrng < 3 hrs
                    set(ca, 'XTick', ceil(96*xlim(1))/96:obj.samplingInterval:xlim(2)) % ticks every 15 minutes
                    obj.datetick(ca, 'HH:MM PM')
            end
        end
        
        function brush_on(varargin)
            obj = varargin{1}(1);
            obj.brushObj = brush_mwdm(obj.fh); % After this, each timeseries axis has ten line objects: 
                                               % the data and its avseries, the three QAQC series, and 
                                               % "virtual" bushing lines (VBL) for each of these; and
                                               % each of the the scatter plot axes now have two "lines": 
                                               % the data and its VBL
            set(obj.brushObj, 'Enable', 'on', 'Color', [1 0 0.1])
            if regexp(version, 'R2011b')
                addlistener(obj.fh, 'WindowMouseRelease', @obj.BrushEnd);
            else
                addlistener(obj.fh, 'windowButtonUp', @obj.BrushEnd);
            end
            obj.hAvLines = findall(obj.fh, 'Tag', 'AvgPlot');
            obj.hBrushLines = findall(obj.fh, 'Tag', 'Brushing'); % This contains objects w/ Tag='Brushing'            
            cmenu = uicontextmenu('Parent', obj.fh, ...
                                  'Serializable', 'off', ...
                                  'Tag', 'BrushSeriesContextMenu');
            mlink = uimenu(cmenu, 'Label', 'Link Selection', ...
                                  'Tag', 'BrushSeriesContextMenuLinkSelection', ...
                                  'Callback', {@obj.disp_var});
                    uimenu(cmenu, 'Label', 'Export Linked', ...
                                  'Tag', 'BrushSeriesContextMenuExportLinked', ...
                                  'Callback', {@obj.exportData, 'linked'}, ...
                                  'Enable', 'off');
                    uimenu(cmenu, 'Label', 'Export Selection', ...
                                  'Tag', 'BrushSeriesContextMenuExportSelected', ...
                                  'Callback', {@obj.exportData, 'selected'});
                    mqaqc = uimenu(cmenu, 'Label', 'Mooring QAQC', ...
                                  'Tag', 'BrushSeriesContextMenuMooringQAQC');
                    if strcmp('Both (No Codes)',...
                            get(get(findall(0,'Tag','button_group'), 'SelectedObject'), 'String'))
                        set(mqaqc, 'Enable', 'off')
                    end
                    uimenu(mqaqc, 'Label', 'Define Flags', ...
                                  'Tag', 'BrushSeriesContextMenuDefineFlags', ...
                                  'Callback', {@obj.define_qaqc});
            for i = 1:length(obj.hBrushLines)
                if isempty(get(obj.hBrushLines(i), 'ZData'))
                    set(obj.hBrushLines(i), 'Tag', '')
                else
                    set(obj.hBrushLines(i), 'UIContextMenu', cmenu)
                    set(get(obj.hBrushLines(i), 'Parent'), 'CLimMode', 'manual');
                end
            end
            set(obj.fh, 'renderer', 'zbuffer')
        end
                
        function BrushEnd(obj, ~, ~)
            datamanager.brushup(obj.fh)
            set(obj.fh, 'renderer', 'zbuffer')
        end
        
        function disp_var(varargin)
            obj = varargin{1}(1);
            DataAxes = findall(obj.fh, 'Tag', 'DataAxes', '-or', 'Tag', 'QAaxes');
            BrushLines = findall(gca, 'Tag', 'Brushing');
            brushedZData = get(BrushLines, 'Zdata');
            if strcmp(get(BrushLines, 'type'), 'surface')%(nr-1)*(nc-1) % if neither nr nor nc == 1
                oneD_brushedZData = brushedZData(:,1);
                for i=1:obj.nQAlines
                    indices = (i-1)*obj.nPts + 1 : 1 : i*obj.nPts;
                    oneD_brushedZData = brushedZData(indices);
                    if any(~isnan(oneD_brushedZData))
                        break;
                    end
                end
                tmp = [oneD_brushedZData; oneD_brushedZData];
                multiD_brushedZData = [tmp tmp tmp]';
            else
                if iscell(brushedZData)
                    tmp = cellfun(@isnan, brushedZData, 'UniformOutput', false);
                    brushedZData = [brushedZData{~cellfun(@all, tmp)}];
                end
                oneD_brushedZData = brushedZData;
                multiD_brushedZData = [[brushedZData brushedZData brushedZData];...
                                       [brushedZData brushedZData brushedZData]]';
            end
            % Ensure that brushed selection is plotted on top of individual
            % data elements
            brushDataHeight = 1e10;
            for ax = DataAxes
                set(ax, 'ZLim', [0 brushDataHeight]);
            end
            oneD_brushedZData(~isnan(oneD_brushedZData)) = brushDataHeight;
            multiD_brushedZData(~isnan(multiD_brushedZData)) = brushDataHeight;
            if numel(oneD_brushedZData)>0
                for i = 1:length(DataAxes)
                    if DataAxes(i) ~= gca
                        hDataPlot_gca = findall(DataAxes(i), 'Tag', 'DataPlot', ...
                                                '-or', 'Tag', 'QAplot');
                        hBrushLine_gca = findall(DataAxes(i), 'Tag', 'Brushing');
                        brushedXData = get(hDataPlot_gca, 'XData');
                        brushedYData = get(hDataPlot_gca, 'YData');
                        try
                            brushedCData = get(hDataPlot_gca, 'CData');
                        catch
                        end
                        if isequal(get(hDataPlot_gca, 'type'), 'surface') && ...
                                ~strcmp('Both (No Codes)',...
                                get(get(findall(0,'Tag','button_group'),...
                                'SelectedObject'), 'String'))
                            brushedXData(isnan(multiD_brushedZData)) = nan;
                            brushedYData(isnan(multiD_brushedZData)) = nan;
                            set(hBrushLine_gca, 'Xdata', brushedXData,...
                                                'Ydata', brushedYData,... 
                                                'Zdata', -multiD_brushedZData,...
                                                'MarkerEdgeColor', 'g',...
                                                'MarkerFaceColor', 'g',...
                                                'MarkerSize', 3.5,...
                                                'EdgeColor', 'none')
                        elseif ~isequal(get(hDataPlot_gca, 'type'), 'surface')
                            brushedXData(isnan(oneD_brushedZData)) = nan;
                            brushedYData(isnan(oneD_brushedZData)) = nan;
                            set(hBrushLine_gca, 'Xdata', brushedXData,...
                                                'Ydata', brushedYData,...
                                                'Zdata', oneD_brushedZData,...
                                                'Color', 'g',...
                                                'MarkerEdgeColor', 'g',...
                                                'MarkerFaceColor', 'g',...
                                                'MarkerSize', 3.5)
                        end
                    else
                        hDataPlot_gca = findall(DataAxes(i), 'Tag', 'DataPlot', ...
                                                '-or', 'Tag', 'QAplot');
                        if isequal(get(hDataPlot_gca, 'type'), 'surface')
                            hBrushLine_gca = findall(DataAxes(i), 'Tag', 'Brushing');
                            brushedXData = get(hDataPlot_gca, 'XData');
                            brushedYData = get(hDataPlot_gca, 'YData');
                            brushedXData(isnan(multiD_brushedZData)) = nan;
                            brushedYData(isnan(multiD_brushedZData)) = nan;
                            set(hBrushLine_gca, 'Xdata', brushedXData,...
                                                'Ydata', brushedYData,... 
                                                'Zdata', multiD_brushedZData,...
                                                'MarkerEdgeColor', [1 0 0.1],...
                                                'MarkerFaceColor', [1 0 0.1],...
                                                'MarkerSize', 3.5,...
                                                'EdgeColor', 'none')
                        end
                    end
                end
            end
            set(obj.fh, 'renderer', 'zbuffer')
        end
        
        function define_qaqc(varargin)
            global uniqueCodes %extantQAcodes 
            obj = varargin{1}(1);
          % Get the required plots and initialize the storage structure
            qaPlots = findall(obj.fh, 'Tag', 'QAplot')';
            uniqueCodes = struct('P', struct('vl', {{}}, 'fl', {{}}, 'cl', {{}}), ...
                                 'T', struct('vl', {{}}, 'fl', {{}}, 'cl', {{}}), ...
                                 'S', struct('vl', {{}}, 'fl', {{}}, 'cl', {{}}), ...
                                 'O', struct('vl', {{}}, 'fl', {{}}, 'cl', {{}}), ...
                                 'figCntnr', obj);
          % All of the below is for pre-populating the QAQC-setting GUI
            for qaplot = qaPlots
                type = get(qaplot, 'UserData');
                tmp = get(get(qaplot, 'Parent'), 'Children');
                tmp = get(tmp(tmp~=qaplot), 'ZData');
                obj.brushedQA.(type).mask = tmp;
                [nr,~] = size(tmp); nr = nr/3;
                brushedcl = tmp(1:nr,:); 
                brushedflgs = tmp(nr+1:2*nr,:); 
                brushedvals = tmp(2*nr+1:3*nr,:);
                data = get(qaplot, 'CData');
                obj.brushedQA.(type).data = data;
                cldata = data(1:nr,:);
                flgdata = data(nr+1:2*nr,:); 
                valdata = data(2*nr+1:3*nr,:);
                tmp = ~isnan(brushedcl);% nr = sum(tmp(:,1));
                uniqueCodes.(type).cl = {nanUnique(cldata(tmp))};
                tmp = ~isnan(brushedflgs);% nr = sum(tmp(:,1));
                uniqueCodes.(type).fl = {nanUnique(flgdata(tmp))};
                tmp = ~isnan(brushedvals);% nr = sum(tmp(:,1));
                uniqueCodes.(type).vl = {nanUnique(valdata(tmp))};             
              % Note: extantQAcodes will now contain the "safety" (undo) values 'til either:
              % the "Commit" button is pressed, or 
              % this GUI instance is destroyed
            end
            qaGUI = QAQC_GUI();
        end
        
        function success = exportData(varargin)
            obj = varargin{1};
            what2export = varargin{4};
            success = false;
            [outFilename, outPath, ~] = uiputfile('*.asc; *.csv; *.tsv; *.txt', 'File to save to');
            if ~isempty(outPath)
                out = fopen(fullfile(outPath, outFilename), 'wt');
                if out ~= -1
                    if strcmp(what2export, 'selected')
                      % Get the data and QA plots for the selected parameter
                      % TODO: this doesn't get the line plot if the QA plot is what was brushed
                        bPlots = findall(obj.fh, 'Color', [1 0 0.1], '-or', ...
                                                 'FaceColor', [1 0 0.1]);
                        paramPlot = findobj(bPlots, 'Type', 'line');
                        parAx = get(paramPlot, 'Parent');
                        param = get(findobj(parAx, 'Tag', 'DataPlot'), 'UserData');
                        QAplot = findobj(obj.fh, 'UserData', param, 'Type', 'surface');
                      % Get the data
                        times = get(paramPlot, 'XData');
                        Npts = numel(times);
                        vals = get(paramPlot, 'YData');
                        QAdata = get(QAplot, 'Cdata');
                        QAvals = QAdata(1:Npts);
                        QAflgs = QAdata(Npts+1:2*Npts);
                        QAclvl = QAdata(2*Npts+1:3*Npts);
                      % Now figure the brushed indices and use them to pare down the data
                        bIndices = find(~isnan(times));
                        times = reshape(times(bIndices), [], 1);
                        vals = reshape(vals(bIndices), [], 1);
                        QAvals = reshape(QAvals(bIndices), [], 1);
                        QAflgs = reshape(QAflgs(bIndices), [], 1);
                        QAclvl = reshape(QAclvl(bIndices), [], 1);
                        switch param
                            case 'P'
                                param = 'Pressure';
                            case 'T'
                                param = 'Temperature';
                            case 'S'
                                param = 'Salinity';
                            case 'O'
                                param = 'Dissolved Oxygen';
                        end
                        fprintf(out, [param '\tQAQC Value\tQAQC Flag\tQAQC Ctrl Lev\tDate\tTime\n']);
                        output = [num2str([vals QAvals QAflgs QAclvl]) ...
                                  datestr(times, '   mm/dd/yyyy   ') ...
                                  datestr(times, 13)];
                        [nr, ~] = size(output);
                        for i=1:nr
                            fprintf(out, '%s\n', output(i,:));
                        end
                    else
                        for ts = {obj.Pvst, obj.Tvst, obj.Svst, obj.Ovst}
                            disp(get(ts{:}.plot, 'UserData')) % This contains the parameter identifier
                        end    
                    end
                    fclose(out);
                else
                    warndlg(['Sorry, couldn''t open ' fullfile(outPath, outFilename) char(10) ...
                             'with write access; export aborted!'])
                end
            end
        end
        
    end
    
end